package com.renhe.utils.baowang;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpStatus;
import org.apache.http.NameValuePair;
import org.apache.http.NoHttpResponseException;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.slf4j.LoggerFactory;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.SocketException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.security.KeyStore;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;


/**
 * HttpClient
 *
 * @author huan
 */
public class HttpSender {

    //private static final org.slf4j.Logger logger = LoggerFactory.getLogger("HttpSender");
    private static final int SOCKET_TIMEOUT = 90000;        //响应超时时间
    private static final String CHARSET = "UTF-8";          //默认UTF-8编码
    public static final String HTTP_GET_METHOD = "get";
    public static final String HTTP_POST_METHOD = "post";
    public static final String HTTP_PUT_METHOD = "put";
    public static final String HTTP_HEAD_METHOD = "head";
    public static final String HTTP_DELETE_METHOD = "delete";

    public static final String proxyHost = "proxyHost";
    public static final String proxyPort = "proxyPort";
    public static final String proxyName = "proxyName";
    public static final String proxyPass = "proxyPass";


    public static int defaultConnectTimeout = 90000;
    public static int defaultReadTimeout = 90000;

    public static Map<String, CloseableHttpClient> httpClients = new ConcurrentHashMap<>();

    private static final String defaultHttpClientSocketTimeoutKey = "defaultHttpClientSocketTimeout";
    public static int defaultHttpClientSocketTimeout = 15000;
    private static final String defaultHttpClientConnectTimeoutKey = "defaultHttpClientConnectTimeout";
    public static int defaultHttpClientConnectTimeout = 15000;
    private static final String defaultHttpClientConnectionRequestTimeoutKey = "defaultHttpClientConnectionRequestTimeout";
    public static int defaultHttpClientConnectionRequestTimeout = 15000;

    private static PoolingHttpClientConnectionManager ccm;
    private static HashMap<String, String> HEADERS = new HashMap<String, String>() {
        {
            put("Content-Type", "application/json;charset=utf-8");
        }
    };
    private static LayeredConnectionSocketFactory sslSocketFactory = null;

    private static RequestConfig defaultRequestConfig = null;

    static {
        try {
            //logger.info("设置前系统socket连接时间：sun.net.client.defaultConnectTimeout==" + System.getProperty("sun.net.client.defaultConnectTimeout"));
            //logger.info("设置前系统socket连接时间：sun.net.client.defaultReadTimeout==" + System.getProperty("sun.net.client.defaultReadTimeout"));
            System.setProperty("sun.net.client.defaultConnectTimeout", String.valueOf(defaultConnectTimeout));// （单位：毫秒）
            System.setProperty("sun.net.client.defaultReadTimeout", String.valueOf(defaultReadTimeout)); // （单位：毫秒）
            defaultRequestConfig = RequestConfig.custom()
                    .setSocketTimeout(defaultHttpClientSocketTimeout)
                    .setConnectTimeout(defaultHttpClientConnectTimeout)
                    .setConnectionRequestTimeout(defaultHttpClientConnectionRequestTimeout)
                    .setStaleConnectionCheckEnabled(true)
                    .build();
            //logger.info("设置后系统socket连接时间：sun.net.client.defaultConnectTimeout==" + System.getProperty("sun.net.client.defaultConnectTimeout"));
            //logger.info("设置后系统socket连接时间：sun.net.client.defaultReadTimeout==" + System.getProperty("sun.net.client.defaultReadTimeout"));
//            System.setProperty("https.protocols", "SSLv2Hello,SSLv3,TLSv1,TLSv1.1,TLSv1.2");
            //http明文报文Socket工厂
            ConnectionSocketFactory plainSf = PlainConnectionSocketFactory.getSocketFactory();
            //加密上下文
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, new TrustManager[]{new MyX509TrustManager()}, null);
            sslSocketFactory = new SSLConnectionSocketFactory(
                    sslContext,
                    new String[]{"TLSv1", "SSLv3", "SSLv2Hello"},
                    null,
                    SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
//            LayeredConnectionSocketFactory sslSocketFactory1 = new SSLConnectionSocketFactory(sslContext, SSLConnectionSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainSf).register("https", sslSocketFactory).build();
            ccm = new PoolingHttpClientConnectionManager(r);
            //like https.protocols=SSLv3,TLSv1) it will start with TLSv1.

            ccm.setMaxTotal(5000); //将最大连接数增加到300
            ccm.setDefaultMaxPerRoute(1000); //将每个路由基础的连接增加到20
            ccm.setDefaultSocketConfig(SocketConfig.custom().setSoTimeout(defaultReadTimeout).build());
        } catch (Exception e) {
            //logger.error("HTTPCLIENT 初始化异常 ", e);
            throw new RuntimeException("HTTPCLIENT 初始化异常");
        }
    }

    private static Map<String, LayeredConnectionSocketFactory> sslSocketFactoryMap = new HashMap<String, LayeredConnectionSocketFactory>();

    public static CloseableHttpClient buildHttpClientByComId(String insComId) {
        try {
            if (sslSocketFactoryMap.containsKey(insComId)) {

            } else {
                String defaultPwd = "123456";
                SSLContext sslContext2 = SSLContext.getInstance("SSL");
                KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
                TrustManagerFactory tmf = TrustManagerFactory
                        .getInstance("SunX509");
                KeyStore ks = KeyStore.getInstance("JKS");
                String path = HttpSender.class.getResource("/keys/" + insComId + ".jks").getFile();
                ks.load(new FileInputStream(path),
                        defaultPwd.toCharArray());

                kmf.init(ks, defaultPwd.toCharArray());
                KeyStore tks = KeyStore.getInstance("JKS");
                tks.load(new FileInputStream(path),
                        defaultPwd.toCharArray());
                tmf.init(tks);
                sslContext2.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
                LayeredConnectionSocketFactory sslSocketFactory2 = new SSLConnectionSocketFactory(
                        sslContext2);
                sslSocketFactoryMap.put(insComId, sslSocketFactory2);

            }
            return HttpClients.custom().setSSLSocketFactory(sslSocketFactoryMap.get(insComId)).build();

        } catch (Exception ex) {
            //logger.error("加载专用的http请求出现异常", ex);
            return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
        }
    }


    public static CloseableHttpClient buildHttpClient() {
        return buildHttpClient(true, "");
    }

    public static CloseableHttpClient buildHttpClient(boolean usePoolConnect, String keepSessionKey) {

        if (StringUtils.isNoneEmpty(keepSessionKey)) {
            if (httpClients.containsKey(keepSessionKey))
                return httpClients.get(keepSessionKey);
            else {
                CloseableHttpClient closeableHttpClient;
                if (usePoolConnect) {
                    closeableHttpClient = HttpClients.custom().setConnectionManager(ccm).setConnectionManagerShared(true).build();
                } else {
                    closeableHttpClient = HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
                }
                httpClients.put(keepSessionKey, closeableHttpClient);
                return closeableHttpClient;
            }
        } else {
            if (usePoolConnect)
                return HttpClients.custom().setConnectionManager(ccm).setConnectionManagerShared(true).build();
            else {
                return HttpClients.custom().setSSLSocketFactory(sslSocketFactory).build();
            }
        }
    }

    public static CloseableHttpClient buildHttpClient(String proxyHost, Integer proxyPort, String proxyName, String proxyPass) {
//        defaultRequestConfig = defaultRequestConfig.copy(defaultRequestConfig).setProxy(new HttpHost(proxyHost,proxyPort)).build();
        return buildHttpClient(true, "", proxyHost, proxyPort, proxyName, proxyPass);
    }

    public static CloseableHttpClient buildHttpClient(boolean usePoolConnect, String keepSessionKey, String proxyHost, Integer proxyPort, String proxyName, String proxyPass) {
        CredentialsProvider credsProvider = new BasicCredentialsProvider();
        credsProvider.setCredentials(new AuthScope(proxyHost, proxyPort), new UsernamePasswordCredentials(proxyName, proxyPass));
        if (StringUtils.isNoneEmpty(keepSessionKey)) {
            if (httpClients.containsKey(keepSessionKey))
                return httpClients.get(keepSessionKey);
            else {
                CloseableHttpClient closeableHttpClient;
                if (usePoolConnect) {
                    closeableHttpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).setConnectionManager(ccm).setConnectionManagerShared(true).build();
                } else {
                    closeableHttpClient = HttpClients.custom().setDefaultCredentialsProvider(credsProvider).setSSLSocketFactory(sslSocketFactory).build();
                }
                httpClients.put(keepSessionKey, closeableHttpClient);
                return closeableHttpClient;
            }
        } else {
            if (usePoolConnect)
                return HttpClients.custom().setDefaultCredentialsProvider(credsProvider).setConnectionManager(ccm).setConnectionManagerShared(true).build();
            else {
                return HttpClients.custom().setDefaultCredentialsProvider(credsProvider).setSSLSocketFactory(sslSocketFactory).build();
            }
        }
    }


    public static Object doGet(String url, Map<String, Object> params, Boolean isFile) throws Exception {
        return doGet(url, params, CHARSET, isFile);
    }

    public static String doGet(String url, Map<String, Object> params) throws Exception {
        return doGet(url, params, CHARSET);
    }

    public static String doPost(String url, String sendStr) throws Exception {
        return doPost(true, url, sendStr, null, HEADERS, CHARSET, getDefaultRequestConfig());
    }

    /**
     * Http Get 获取内容
     *
     * @param url     请求的url地址
     * @param params  请求的参数
     * @param charset 编码格式
     * @return 响应内容
     */
    public static String doGet(String url, Map<String, Object> params, String charset) throws Exception {
        return doGet(buildHttpClient(), url, params, charset);
    }

    public static Object doGet(String url, Map<String, Object> params, String charset, Boolean isFile) throws Exception {
        return doGet(buildHttpClient(), url, params, charset, getDefaultRequestConfig(), isFile);
    }

    public static RequestConfig getDefaultRequestConfig() {
        return defaultRequestConfig;
    }

    public static String doGet(CloseableHttpClient httpClient, String url, Map<String, Object> params, String charset) throws Exception {
        return (String) doGet(httpClient, url, params, charset, getDefaultRequestConfig(), false);
    }

    public static Object doGetWithRetry(int retryTimes, CloseableHttpClient httpClient, String url, Map<String, Object> params, String charset, RequestConfig requestConfig, Boolean isFile) throws Exception {
        int index = 1;
        while (index++ <= retryTimes) {
            try {
                return doGet(httpClient, url, params, charset, requestConfig, isFile);
            } catch (UnknownHostException | ConnectTimeoutException | SocketException | SocketTimeoutException | NoHttpResponseException e) {
                //logger.warn("当前地址:{}的GET请求出现网络异常,将进行第{}次重试!", url, index);
                TimeUnit.SECONDS.sleep(index * 5);
            }
        }
        return null;
    }

    /**
     * @param httpClient 链接
     * @param url        url地址
     * @return 返回结果对象
     * @throws Exception 抛出异常
     */
    public static Object doGet(CloseableHttpClient httpClient, String url) throws Exception {
        return doGet(httpClient, url, null, "UTF-8", null, false);
    }

    public static Object doGet(CloseableHttpClient httpClient, String url, Map<String, Object> params, String charset, RequestConfig requestConfig, Boolean isFile) throws Exception {
        return doGet(httpClient, url, null, params, charset, requestConfig, isFile);
    }

    public static Object doGet(CloseableHttpClient httpClient, String url, Map<String, String> headers, Map<String, Object> params, String charset, RequestConfig requestConfig, Boolean isFile) throws Exception {
        return doIt(HTTP_GET_METHOD, httpClient, url, headers, params, charset, requestConfig, isFile);
    }

    public static Object doDelete(CloseableHttpClient httpClient, String url, Map<String, String> headers, Map<String, Object> params, String charset, RequestConfig requestConfig, Boolean isFile) throws Exception {
        return doIt(HTTP_DELETE_METHOD, httpClient, url, headers, params, charset, requestConfig, isFile);
    }

    private static Object doIt(String requestType, CloseableHttpClient httpClient, String url, Map<String, String> headers, Map<String, Object> params, String charset, RequestConfig requestConfig, Boolean isFile) throws Exception {
        if (StringUtils.isEmpty(url)) return null;
        if (httpClient == null) {
            httpClient = buildHttpClient();
        }
        CloseableHttpResponse response = null;
        String result = "";
        HttpRequestBase httpRequestBase = null;
        try {
            if (params != null && !params.isEmpty()) {
                List<NameValuePair> pairs = new ArrayList<NameValuePair>(params.size());
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    final String key = entry.getKey();
                    if (StringUtils.isNotEmpty(key)) {
                        pairs.add(new BasicNameValuePair(key, entry.getValue() + ""));
                    }
                }
                if (url.contains("?")) {
                    url += "&" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));
                } else {
                    url += "?" + EntityUtils.toString(new UrlEncodedFormEntity(pairs, charset));
                }
            }
            if (HTTP_GET_METHOD.equals(requestType)) {
                httpRequestBase = new HttpGet(url);
            } else if (HTTP_DELETE_METHOD.equals(requestType)) {
                httpRequestBase = new HttpDelete(url);
            } else {
                httpRequestBase = new HttpGet(url);
            }
            if (headers != null && !headers.isEmpty()) {
                for (Map.Entry<String, String> header : headers.entrySet()) {
                    httpRequestBase.addHeader(header.getKey(), header.getValue());
                }
            }
            if (requestConfig == null) {
                requestConfig = defaultRequestConfig;
            }
            httpRequestBase.setConfig(requestConfig);
            response = httpClient.execute(httpRequestBase);
            int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_MOVED_PERMANENTLY == statusCode || HttpStatus.SC_MOVED_TEMPORARILY == statusCode || HttpStatus.SC_SEE_OTHER == statusCode || HttpStatus.SC_TEMPORARY_REDIRECT == statusCode) {

                if (null == response.getLastHeader("location") || "".equals(response.getLastHeader("location").getValue())) {
                    //logger.error("返回的HTTP STATUS 为:statusCode,但是location跳转地址为空!");
                } else {
                    String locationUrl = response.getLastHeader("location").getValue();
                    //logger.info("请求{}跳转,跳转地址:{}", statusCode, locationUrl);
                    if (HttpStatus.SC_TEMPORARY_REDIRECT == 307) {
                        return doPost(httpClient, locationUrl, null, null, headers, charset);
                    } else {
                        return doGet(httpClient, locationUrl, null, charset, requestConfig, false);
                    }
                }
            } else if (statusCode != 200 && statusCode != 500) {
                httpRequestBase.abort();
                throw new Exception("http request had failed , failed statusCode :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                if (!isFile) {
                    result = EntityUtils.toString(entity, charset);
                } else {
                    try (final InputStream instream = entity.getContent()) {
                        return FileUtil.getStreamAsByteArray(instream);
                    }
                }
            }
            EntityUtils.consume(entity);
            return result;
        } catch (Exception e) {
            if (httpRequestBase != null && !httpRequestBase.isAborted()) {
                httpRequestBase.abort();
            }
            //logger.error("HttpClient Get Error:", e);
            throw e;
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    //logger.error("回收httpConnection时发生异常", e);
                }
            }
            if (httpRequestBase != null) {
                try {
                    httpRequestBase.releaseConnection();
                } catch (Exception e) {
                    //logger.error("释放httpConnection时发生异常", e);
                }
            }
        }
    }

    public static String doPost(String url, String sendStr, Map<String, String> headers, String charset) throws Exception {
        return doPost(true, url, sendStr, null, headers, charset, defaultRequestConfig);
    }

    public static String doPost(String url, String sendStr, Map<String, String> headers, String charset, RequestConfig requestConfig) throws Exception {
        return doPost(true, url, sendStr, null, headers, charset, requestConfig);
    }

    public static String doPost(String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset) throws Exception {
        return doPost(true, url, sendStr, params, headers, charset, defaultRequestConfig);
    }

    public static String doPost(String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset, RequestConfig requestConfig) throws Exception {
        return doPost(true, url, sendStr, params, headers, charset, requestConfig);
    }

    /**
     * Http Post 获取内容
     *
     * @param usePoolConnect 是否需要缓存连接
     * @param url            请求的url地址
     * @param sendStr        请求的参数
     * @param params         请求头文件
     * @param charset        编码格式
     * @return 响应内容
     */
    public static String doPost(boolean usePoolConnect, String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset) throws Exception {
        return doPost(usePoolConnect, url, sendStr, params, headers, charset, getDefaultRequestConfig());

    }

    public static String doPost(boolean usePoolConnect, String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset, RequestConfig requestConfig) throws Exception {
        return doPost(usePoolConnect, url, sendStr, params, headers, charset, requestConfig, "");
    }

    public static String doPost(boolean usePoolConnect, String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey) throws Exception {
        return doPost(null, usePoolConnect, url, sendStr, params, headers, charset, requestConfig, "");
    }

    public static String doPost(CloseableHttpClient closeableHttpClient, String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset) throws Exception {
        return doPost(closeableHttpClient, true, url, sendStr, params, headers, charset, null, "");
    }

    public static String doPostWithRepHeaders(CloseableHttpClient closeableHttpClient, String url, String sendStr, Map<String, String> params, Map<String, String> headers, String charset, Map<String, String> repHeaders) throws Exception {
        return doPostWithRepHeaders(closeableHttpClient, true, url, sendStr, params, headers, charset, null, "", repHeaders);
    }


    /**
     * Http Post 获取内容
     *
     * @param usePoolConnect 是否需要缓存连接
     * @param url            请求的url地址
     * @param sendStr        请求的参数
     * @param params         请求头文件
     * @param charset        编码格式
     * @param keepSessionKey 保持的session的Key
     * @return 响应内容
     */
    public static String doPost(CloseableHttpClient closeableHttpClient, boolean usePoolConnect, String url, String sendStr, Object params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey) throws Exception {
        return doPostWithRepHeaders(closeableHttpClient, usePoolConnect, url, sendStr, params, headers, charset, requestConfig, keepSessionKey, null);
    }

    public static String doPostWithRepHeaders(CloseableHttpClient closeableHttpClient, boolean usePoolConnect, String url, String sendStr, Object params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey, Map<String, String> repHeaders) throws Exception {
        return doItWithRepHeaders(HTTP_POST_METHOD, closeableHttpClient, usePoolConnect, url, sendStr, params, headers, charset, requestConfig, keepSessionKey, repHeaders);
    }

    public static String doPut(CloseableHttpClient closeableHttpClient, boolean usePoolConnect, String url, String sendStr, Object params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey, Map<String, String> repHeaders) throws Exception {
        return doItWithRepHeaders(HTTP_PUT_METHOD, closeableHttpClient, usePoolConnect, url, sendStr, params, headers, charset, requestConfig, keepSessionKey, repHeaders);
    }

    public static String doPut(String url, String sendStr, Map<String, String> headers, String charset) throws Exception {
        return doItWithRepHeaders(HTTP_PUT_METHOD, null, true, url, sendStr, null, headers, charset, null, "", null);
    }

    public static String doPut(String url, Object params, Map<String, String> headers, String charset) throws Exception {
        return doItWithRepHeaders(HTTP_PUT_METHOD, null, true, url, null, params, headers, charset, null, "", null);
    }

    public static String doItWithRepHeaders(String requestMethod, CloseableHttpClient closeableHttpClient, boolean usePoolConnect, String url, String sendStr, Object params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey, Map<String, String> repHeaders) throws Exception {
        if (StringUtils.isEmpty(url)) return null;
        CloseableHttpClient httpClient;
        if (closeableHttpClient == null) {
            httpClient = buildHttpClient(usePoolConnect, keepSessionKey);
        } else {
            httpClient = closeableHttpClient;
        }
        CloseableHttpResponse response = null;
        String result = null;
        HttpEntityEnclosingRequestBase httpRequestBase = null;
        try {
            if (HTTP_POST_METHOD.equals(requestMethod)) {
                httpRequestBase = new HttpPost(url);
            } else if (HTTP_PUT_METHOD.equals(requestMethod)) {
                httpRequestBase = new HttpPut(url);
            }
            if (headers != null) {
                for (String header : headers.keySet()) {
                    httpRequestBase.setHeader(header, headers.get(header));
                }
            }
            if (params != null && params instanceof Map && ((Map) params).size() > 0) {
                buildEntity(httpRequestBase, (Map) params, charset);
            } else if (params != null && params instanceof List && ((List) params).size() > 0) {
                buildEntity(httpRequestBase, (List) params, charset);
            } else if (StringUtils.isNoneEmpty(sendStr)) {
                httpRequestBase.setEntity(new StringEntity(sendStr, charset));
            }
            if (requestConfig == null) {
                requestConfig = defaultRequestConfig;
            }
            httpRequestBase.setConfig(requestConfig);
            response = httpClient.execute(httpRequestBase);

            int statusCode = response.getStatusLine().getStatusCode();
            if (HttpStatus.SC_MOVED_PERMANENTLY == statusCode || HttpStatus.SC_MOVED_TEMPORARILY == statusCode || HttpStatus.SC_SEE_OTHER == statusCode || HttpStatus.SC_TEMPORARY_REDIRECT == statusCode) {

                if (null == response.getLastHeader("location") || "".equals(response.getLastHeader("location").getValue())) {
                    //logger.error("返回的HTTP STATUS 为:statusCode,但是location跳转地址为空!");
                } else {
                    String locationUrl = response.getLastHeader("location").getValue();
                    //logger.info("请求{},跳转地址:{}", statusCode, locationUrl);
                    if (HttpStatus.SC_TEMPORARY_REDIRECT == statusCode) {
                        return doPost(httpClient, locationUrl, null, null, headers, charset);
                    } else {
                        return (String) doGet(httpClient, locationUrl, null, charset, requestConfig, false);
                    }
                }
            } else if (statusCode != 200 && statusCode != 500) {
                httpRequestBase.abort();
                throw new Exception("http request had failed , failed statusCode :" + statusCode);
            }
            HttpEntity entity = response.getEntity();
            if (repHeaders != null) {
                Arrays.stream(response.getAllHeaders()).forEach(header -> repHeaders.put(header.getName(), header.getValue()));
            }
            if (entity != null) {
                result = EntityUtils.toString(entity, charset);
            }
            EntityUtils.consume(entity);
        } catch (Exception e) {
            if (httpRequestBase != null && !httpRequestBase.isAborted()) {
                httpRequestBase.abort();
            }
            //logger.error("HttpClient Post Error:", e);
            throw e;
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    //logger.error("回收httpConnection时发生异常", e);
                }
            }
            if (httpRequestBase != null) {
                try {
                    httpRequestBase.releaseConnection();
                } catch (Exception e) {
                    //logger.error("释放httpConnection时发生异常", e);
                }
            }
            if (!usePoolConnect && httpClient != null) {
                try {
                    httpClient.close();
                    //logger.debug("释放链接httpClient");
                } catch (Exception e) {
                    //logger.error("释放链接httpClient时发生异常", e);
                }
            }
        }
        return result;
    }

    private static void buildEntity(HttpEntityEnclosingRequestBase http, List<Map<String, String>> params, String charset) throws UnsupportedEncodingException {
        List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
        for (Map<String, String> param : params) {
            for (Map.Entry<String, String> paire : param.entrySet()) {
                nameValuePairs.add(new BasicNameValuePair(paire.getKey(), paire.getValue()));
            }
        }
        http.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
    }

    private static void buildEntity(HttpEntityEnclosingRequestBase http, Map<String, Object> params, String charset) throws UnsupportedEncodingException {
        List<NameValuePair> nameValuePairs = new ArrayList<>();
        builderPair("", params, nameValuePairs);
        http.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
    }

    private static void buildEntity(HttpPut httpPut, Map<String, Object> params, String charset) throws UnsupportedEncodingException {
        List<NameValuePair> nameValuePairs = new ArrayList<>();
        builderPair("", params, nameValuePairs);
        httpPut.setEntity(new UrlEncodedFormEntity(nameValuePairs, charset));
    }

    private static void builderPair(String key, Object params, List<NameValuePair> nameValuePairs) {
        if (params == null) {
            return;
        }
        if (params instanceof List) {
            if (((List) params).size() < 1) {
                return;
            }
            ((List) params).forEach(item -> {
                if (item instanceof Map) {
                    builderPair("", item, nameValuePairs);
                } else if (item instanceof List) {
                    builderPair(key, item, nameValuePairs);
                } else {
                    nameValuePairs.add(new BasicNameValuePair(key, (String) item));
                }
            });

        } else if (params instanceof Map) {
            if (((Map) params).size() < 1) {
                return;
            }
            ((Map) params).forEach((paramKey, value) -> {
                if (value instanceof Map) {
                    builderPair("", value, nameValuePairs);
                } else if (value instanceof List) {
                    builderPair((String) paramKey, value, nameValuePairs);
                } else {
                    nameValuePairs.add(new BasicNameValuePair((String) paramKey, (String) value));
                }
            });
        }
    }

    public static String doPostWithRetryAndLoadHeader(int retryTimes, CloseableHttpClient closeableHttpClient, boolean usePoolConnect, String url, String sendStr, Object params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey, Map<String, String> repHeaders) throws Exception {
        int index = 1;
        while (index++ <= retryTimes) {
            try {
                return doPostWithRepHeaders(closeableHttpClient, usePoolConnect, url, sendStr, params, headers, charset, requestConfig, keepSessionKey, repHeaders);
            } catch (UnknownHostException | ConnectTimeoutException | SocketException | SocketTimeoutException | NoHttpResponseException e) {
                //logger.warn("当前地址:{}的POST请求出现网络异常,将进行第{}次重试!", url, index);
                TimeUnit.SECONDS.sleep(index * 5);
            }
        }
        return null;
    }

    public static String doPostWithRetry(int retryTimes, CloseableHttpClient closeableHttpClient, boolean usePoolConnect, String url, String sendStr, Object params, Map<String, String> headers, String charset, RequestConfig requestConfig, String keepSessionKey) throws Exception {
        int index = 1;
        while (index++ <= retryTimes) {
            try {
                return doPost(closeableHttpClient, usePoolConnect, url, sendStr, params, headers, charset, requestConfig, keepSessionKey);
            } catch (UnknownHostException | ConnectTimeoutException | SocketException | SocketTimeoutException | NoHttpResponseException e) {
                //logger.warn("当前地址:{}的POST请求出现网络异常,将进行第{}次重试!", url, index);
                TimeUnit.SECONDS.sleep(index * 5);
            }
        }
        return null;
    }

}
